home *** CD-ROM | disk | FTP | other *** search
/ PC/CD Gamer UK 120 / CD Gamer Issue 120 (March 2003) (Disc 2).ISO / mods / Q2_Codered / codeRED1_0.exe / Data1.cab / sv_ccmds.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-11-12  |  21.6 KB  |  1,051 lines

  1. /*
  2. Copyright (C) 1997-2001 Id Software, Inc.
  3.  
  4. This program is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU General Public License
  6. as published by the Free Software Foundation; either version 2
  7. of the License, or (at your option) any later version.
  8.  
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
  12.  
  13. See the GNU General Public License for more details.
  14.  
  15. You should have received a copy of the GNU General Public License
  16. along with this program; if not, write to the Free Software
  17. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  18.  
  19. */
  20.  
  21. #include "server.h"
  22.  
  23. /*
  24. ===============================================================================
  25.  
  26. OPERATOR CONSOLE ONLY COMMANDS
  27.  
  28. These commands can only be entered from stdin or by a remote operator datagram
  29. ===============================================================================
  30. */
  31.  
  32. /*
  33. ====================
  34. SV_SetMaster_f
  35.  
  36. Specify a list of master servers
  37. ====================
  38. */
  39. void SV_SetMaster_f (void)
  40. {
  41.     int        i, slot;
  42.  
  43.     // only dedicated servers send heartbeats
  44.     if (!dedicated->value)
  45.     {
  46.         Com_Printf ("Only dedicated servers use masters.\n");
  47.         return;
  48.     }
  49.  
  50.     // make sure the server is listed public
  51.     Cvar_Set ("public", "1");
  52.  
  53.     for (i=1 ; i<MAX_MASTERS ; i++)
  54.         memset (&master_adr[i], 0, sizeof(master_adr[i]));
  55.  
  56.     slot = 1;        // slot 0 will always contain the id master
  57.     for (i=1 ; i<Cmd_Argc() ; i++)
  58.     {
  59.         if (slot == MAX_MASTERS)
  60.             break;
  61.  
  62.         if (!NET_StringToAdr (Cmd_Argv(i), &master_adr[i]))
  63.         {
  64.             Com_Printf ("Bad address: %s\n", Cmd_Argv(i));
  65.             continue;
  66.         }
  67.         if (master_adr[slot].port == 0)
  68.             master_adr[slot].port = BigShort (PORT_MASTER);
  69.  
  70.         Com_Printf ("Master server at %s\n", NET_AdrToString (master_adr[slot]));
  71.  
  72.         Com_Printf ("Sending a ping.\n");
  73.  
  74.         Netchan_OutOfBandPrint (NS_SERVER, master_adr[slot], "ping");
  75.  
  76.         slot++;
  77.     }
  78.  
  79.     svs.last_heartbeat = -9999999;
  80. }
  81.  
  82.  
  83.  
  84. /*
  85. ==================
  86. SV_SetPlayer
  87.  
  88. Sets sv_client and sv_player to the player with idnum Cmd_Argv(1)
  89. ==================
  90. */
  91. qboolean SV_SetPlayer (void)
  92. {
  93.     client_t    *cl;
  94.     int            i;
  95.     int            idnum;
  96.     char        *s;
  97.  
  98.     if (Cmd_Argc() < 2)
  99.         return false;
  100.  
  101.     s = Cmd_Argv(1);
  102.  
  103.     // numeric values are just slot numbers
  104.     if (s[0] >= '0' && s[0] <= '9')
  105.     {
  106.         idnum = atoi(Cmd_Argv(1));
  107.         if (idnum < 0 || idnum >= maxclients->value)
  108.         {
  109.             Com_Printf ("Bad client slot: %i\n", idnum);
  110.             return false;
  111.         }
  112.  
  113.         sv_client = &svs.clients[idnum];
  114.         sv_player = sv_client->edict;
  115.         if (!sv_client->state)
  116.         {
  117.             Com_Printf ("Client %i is not active\n", idnum);
  118.             return false;
  119.         }
  120.         return true;
  121.     }
  122.  
  123.     // check for a name match
  124.     for (i=0,cl=svs.clients ; i<maxclients->value; i++,cl++)
  125.     {
  126.         if (!cl->state)
  127.             continue;
  128.         if (!strcmp(cl->name, s))
  129.         {
  130.             sv_client = cl;
  131.             sv_player = sv_client->edict;
  132.             return true;
  133.         }
  134.     }
  135.  
  136.     Com_Printf ("Userid %s is not on the server\n", s);
  137.     return false;
  138. }
  139.  
  140.  
  141. /*
  142. ===============================================================================
  143.  
  144. SAVEGAME FILES
  145.  
  146. ===============================================================================
  147. */
  148.  
  149. /*
  150. =====================
  151. SV_WipeSavegame
  152.  
  153. Delete save/<XXX>/
  154. =====================
  155. */
  156. void SV_WipeSavegame (char *savename)
  157. {
  158.     char    name[MAX_OSPATH];
  159.     char    *s;
  160.  
  161.     Com_DPrintf("SV_WipeSaveGame(%s)\n", savename);
  162.  
  163.     Com_sprintf (name, sizeof(name), "%s/save/%s/server.ssv", FS_Gamedir (), savename);
  164.     remove (name);
  165.     Com_sprintf (name, sizeof(name), "%s/save/%s/game.ssv", FS_Gamedir (), savename);
  166.     remove (name);
  167.  
  168.     Com_sprintf (name, sizeof(name), "%s/save/%s/*.sav", FS_Gamedir (), savename);
  169.     s = Sys_FindFirst( name, 0, 0 );
  170.     while (s)
  171.     {
  172.         remove (s);
  173.         s = Sys_FindNext( 0, 0 );
  174.     }
  175.     Sys_FindClose ();
  176.     Com_sprintf (name, sizeof(name), "%s/save/%s/*.sv2", FS_Gamedir (), savename);
  177.     s = Sys_FindFirst(name, 0, 0 );
  178.     while (s)
  179.     {
  180.         remove (s);
  181.         s = Sys_FindNext( 0, 0 );
  182.     }
  183.     Sys_FindClose ();
  184. }
  185.  
  186.  
  187. /*
  188. ================
  189. CopyFile
  190. ================
  191. */
  192. void CopyFile (char *src, char *dst)
  193. {
  194.     FILE    *f1, *f2;
  195.     int        l;
  196.     byte    buffer[65536];
  197.  
  198.     Com_DPrintf ("CopyFile (%s, %s)\n", src, dst);
  199.  
  200.     f1 = fopen (src, "rb");
  201.     if (!f1)
  202.         return;
  203.     f2 = fopen (dst, "wb");
  204.     if (!f2)
  205.     {
  206.         fclose (f1);
  207.         return;
  208.     }
  209.  
  210.     while (1)
  211.     {
  212.         l = fread (buffer, 1, sizeof(buffer), f1);
  213.         if (!l)
  214.             break;
  215.         fwrite (buffer, 1, l, f2);
  216.     }
  217.  
  218.     fclose (f1);
  219.     fclose (f2);
  220. }
  221.  
  222.  
  223. /*
  224. ================
  225. SV_CopySaveGame
  226. ================
  227. */
  228. void SV_CopySaveGame (char *src, char *dst)
  229. {
  230.     char    name[MAX_OSPATH], name2[MAX_OSPATH];
  231.     int        l, len;
  232.     char    *found;
  233.  
  234.     Com_DPrintf("SV_CopySaveGame(%s, %s)\n", src, dst);
  235.  
  236.     SV_WipeSavegame (dst);
  237.  
  238.     // copy the savegame over
  239.     Com_sprintf (name, sizeof(name), "%s/save/%s/server.ssv", FS_Gamedir(), src);
  240.     Com_sprintf (name2, sizeof(name2), "%s/save/%s/server.ssv", FS_Gamedir(), dst);
  241.     FS_CreatePath (name2);
  242.     CopyFile (name, name2);
  243.  
  244.     Com_sprintf (name, sizeof(name), "%s/save/%s/game.ssv", FS_Gamedir(), src);
  245.     Com_sprintf (name2, sizeof(name2), "%s/save/%s/game.ssv", FS_Gamedir(), dst);
  246.     CopyFile (name, name2);
  247.  
  248.     Com_sprintf (name, sizeof(name), "%s/save/%s/", FS_Gamedir(), src);
  249.     len = strlen(name);
  250.     Com_sprintf (name, sizeof(name), "%s/save/%s/*.sav", FS_Gamedir(), src);
  251.     found = Sys_FindFirst(name, 0, 0 );
  252.     while (found)
  253.     {
  254.         strcpy (name+len, found+len);
  255.  
  256.         Com_sprintf (name2, sizeof(name2), "%s/save/%s/%s", FS_Gamedir(), dst, found+len);
  257.         CopyFile (name, name2);
  258.  
  259.         // change sav to sv2
  260.         l = strlen(name);
  261.         strcpy (name+l-3, "sv2");
  262.         l = strlen(name2);
  263.         strcpy (name2+l-3, "sv2");
  264.         CopyFile (name, name2);
  265.  
  266.         found = Sys_FindNext( 0, 0 );
  267.     }
  268.     Sys_FindClose ();
  269. }
  270.  
  271.  
  272. /*
  273. ==============
  274. SV_WriteLevelFile
  275.  
  276. ==============
  277. */
  278. void SV_WriteLevelFile (void)
  279. {
  280.     char    name[MAX_OSPATH];
  281.     FILE    *f;
  282.  
  283.     Com_DPrintf("SV_WriteLevelFile()\n");
  284.  
  285.     Com_sprintf (name, sizeof(name), "%s/save/current/%s.sv2", FS_Gamedir(), sv.name);
  286.     f = fopen(name, "wb");
  287.     if (!f)
  288.     {
  289.         Com_Printf ("Failed to open %s\n", name);
  290.         return;
  291.     }
  292.     fwrite (sv.configstrings, sizeof(sv.configstrings), 1, f);
  293.     CM_WritePortalState (f);
  294.     fclose (f);
  295.  
  296.     Com_sprintf (name, sizeof(name), "%s/save/current/%s.sav", FS_Gamedir(), sv.name);
  297.     ge->WriteLevel (name);
  298. }
  299.  
  300. /*
  301. ==============
  302. SV_ReadLevelFile
  303.  
  304. ==============
  305. */
  306. void SV_ReadLevelFile (void)
  307. {
  308.     char    name[MAX_OSPATH];
  309.     FILE    *f;
  310.  
  311.     Com_DPrintf("SV_ReadLevelFile()\n");
  312.  
  313.     Com_sprintf (name, sizeof(name), "%s/save/current/%s.sv2", FS_Gamedir(), sv.name);
  314.     f = fopen(name, "rb");
  315.     if (!f)
  316.     {
  317.         Com_Printf ("Failed to open %s\n", name);
  318.         return;
  319.     }
  320.     FS_Read (sv.configstrings, sizeof(sv.configstrings), f);
  321.     CM_ReadPortalState (f);
  322.     fclose (f);
  323.  
  324.     Com_sprintf (name, sizeof(name), "%s/save/current/%s.sav", FS_Gamedir(), sv.name);
  325.     ge->ReadLevel (name);
  326. }
  327.  
  328. /*
  329. ==============
  330. SV_WriteServerFile
  331.  
  332. ==============
  333. */
  334. void SV_WriteServerFile (qboolean autosave)
  335. {
  336.     FILE    *f;
  337.     cvar_t    *var;
  338.     char    name[MAX_OSPATH], string[128];
  339.     char    comment[32];
  340.     time_t    aclock;
  341.     struct tm    *newtime;
  342.  
  343.     Com_DPrintf("SV_WriteServerFile(%s)\n", autosave ? "true" : "false");
  344.  
  345.     Com_sprintf (name, sizeof(name), "%s/save/current/server.ssv", FS_Gamedir());
  346.     f = fopen (name, "wb");
  347.     if (!f)
  348.     {
  349.         Com_Printf ("Couldn't write %s\n", name);
  350.         return;
  351.     }
  352.     // write the comment field
  353.     memset (comment, 0, sizeof(comment));
  354.  
  355.     if (!autosave)
  356.     {
  357.         time (&aclock);
  358.         newtime = localtime (&aclock);
  359.         Com_sprintf (comment,sizeof(comment), "%2i:%i%i %2i/%2i  ", newtime->tm_hour
  360.             , newtime->tm_min/10, newtime->tm_min%10,
  361.             newtime->tm_mon+1, newtime->tm_mday);
  362.         strncat (comment, sv.configstrings[CS_NAME], sizeof(comment)-1-strlen(comment) );
  363.     }
  364.     else
  365.     {    // autosaved
  366.         Com_sprintf (comment, sizeof(comment), "ENTERING %s", sv.configstrings[CS_NAME]);
  367.     }
  368.  
  369.     fwrite (comment, 1, sizeof(comment), f);
  370.  
  371.     // write the mapcmd
  372.     fwrite (svs.mapcmd, 1, sizeof(svs.mapcmd), f);
  373.  
  374.     // write all CVAR_LATCH cvars
  375.     // these will be things like coop, skill, deathmatch, etc
  376.     for (var = cvar_vars ; var ; var=var->next)
  377.     {
  378.         if (!(var->flags & CVAR_LATCH))
  379.             continue;
  380.         if (strlen(var->name) >= sizeof(name)-1
  381.             || strlen(var->string) >= sizeof(string)-1)
  382.         {
  383.             Com_Printf ("Cvar too long: %s = %s\n", var->name, var->string);
  384.             continue;
  385.         }
  386.         memset (name, 0, sizeof(name));
  387.         memset (string, 0, sizeof(string));
  388.         strcpy (name, var->name);
  389.         strcpy (string, var->string);
  390.         fwrite (name, 1, sizeof(name), f);
  391.         fwrite (string, 1, sizeof(string), f);
  392.     }
  393.  
  394.     fclose (f);
  395.  
  396.     // write game state
  397.     Com_sprintf (name, sizeof(name), "%s/save/current/game.ssv", FS_Gamedir());
  398.     ge->WriteGame (name, autosave);
  399. }
  400.  
  401. /*
  402. ==============
  403. SV_ReadServerFile
  404.  
  405. ==============
  406. */
  407. void SV_ReadServerFile (void)
  408. {
  409.     FILE    *f;
  410.     char    name[MAX_OSPATH], string[128];
  411.     char    comment[32];
  412.     char    mapcmd[MAX_TOKEN_CHARS];
  413.  
  414.     Com_DPrintf("SV_ReadServerFile()\n");
  415.  
  416.     Com_sprintf (name, sizeof(name), "%s/save/current/server.ssv", FS_Gamedir());
  417.     f = fopen (name, "rb");
  418.     if (!f)
  419.     {
  420.         Com_Printf ("Couldn't read %s\n", name);
  421.         return;
  422.     }
  423.     // read the comment field
  424.     FS_Read (comment, sizeof(comment), f);
  425.  
  426.     // read the mapcmd
  427.     FS_Read (mapcmd, sizeof(mapcmd), f);
  428.  
  429.     // read all CVAR_LATCH cvars
  430.     // these will be things like coop, skill, deathmatch, etc
  431.     while (1)
  432.     {
  433.         if (!fread (name, 1, sizeof(name), f))
  434.             break;
  435.         FS_Read (string, sizeof(string), f);
  436.         Com_DPrintf ("Set %s = %s\n", name, string);
  437.         Cvar_ForceSet (name, string);
  438.     }
  439.  
  440.     fclose (f);
  441.  
  442.     // start a new game fresh with new cvars
  443.     SV_InitGame ();
  444.  
  445.     strcpy (svs.mapcmd, mapcmd);
  446.  
  447.     // read game state
  448.     Com_sprintf (name, sizeof(name), "%s/save/current/game.ssv", FS_Gamedir());
  449.     ge->ReadGame (name);
  450. }
  451.  
  452.  
  453. //=========================================================
  454.  
  455.  
  456.  
  457.  
  458. /*
  459. ==================
  460. SV_DemoMap_f
  461.  
  462. Puts the server in demo mode on a specific map/cinematic
  463. ==================
  464. */
  465. void SV_DemoMap_f (void)
  466. {
  467.     SV_Map (true, Cmd_Argv(1), false );
  468. }
  469.  
  470. /*
  471. ==================
  472. SV_GameMap_f
  473.  
  474. Saves the state of the map just being exited and goes to a new map.
  475.  
  476. If the initial character of the map string is '*', the next map is
  477. in a new unit, so the current savegame directory is cleared of
  478. map files.
  479.  
  480. Example:
  481.  
  482. *inter.cin+jail
  483.  
  484. Clears the archived maps, plays the inter.cin cinematic, then
  485. goes to map jail.bsp.
  486. ==================
  487. */
  488. void SV_GameMap_f (void)
  489. {
  490.     char        *map;
  491.     int            i;
  492.     client_t    *cl;
  493.     qboolean    *savedInuse;
  494.  
  495.     if (Cmd_Argc() != 2)
  496.     {
  497.         Com_Printf ("USAGE: gamemap <map>\n");
  498.         return;
  499.     }
  500.  
  501.     Com_DPrintf("SV_GameMap(%s)\n", Cmd_Argv(1));
  502.  
  503.     FS_CreatePath (va("%s/save/current/", FS_Gamedir()));
  504.  
  505.     // check for clearing the current savegame
  506.     map = Cmd_Argv(1);
  507.     if (map[0] == '*')
  508.     {
  509.         // wipe all the *.sav files
  510.         SV_WipeSavegame ("current");
  511.     }
  512.     else
  513.     {    // save the map just exited
  514.         if (sv.state == ss_game)
  515.         {
  516.             // clear all the client inuse flags before saving so that
  517.             // when the level is re-entered, the clients will spawn
  518.             // at spawn points instead of occupying body shells
  519.             savedInuse = malloc(maxclients->value * sizeof(qboolean));
  520.             for (i=0,cl=svs.clients ; i<maxclients->value; i++,cl++)
  521.             {
  522.                 savedInuse[i] = cl->edict->inuse;
  523.                 cl->edict->inuse = false;
  524.             }
  525.  
  526.             SV_WriteLevelFile ();
  527.  
  528.             // we must restore these for clients to transfer over correctly
  529.             for (i=0,cl=svs.clients ; i<maxclients->value; i++,cl++)
  530.                 cl->edict->inuse = savedInuse[i];
  531.             free (savedInuse);
  532.         }
  533.     }
  534.  
  535.     // start up the next map
  536.     SV_Map (false, Cmd_Argv(1), false );
  537.  
  538.     // archive server state
  539.     strncpy (svs.mapcmd, Cmd_Argv(1), sizeof(svs.mapcmd)-1);
  540.  
  541.     // copy off the level to the autosave slot
  542.     if (!dedicated->value)
  543.     {
  544.         SV_WriteServerFile (true);
  545.         SV_CopySaveGame ("current", "save0");
  546.     }
  547. }
  548.  
  549. /*
  550. ==================
  551. SV_Map_f
  552.  
  553. Goes directly to a given map without any savegame archiving.
  554. For development work
  555. ==================
  556. */
  557. void SV_Map_f (void)
  558. {
  559.     char    *map;
  560.     char    expanded[MAX_QPATH];
  561.  
  562.     // if not a pcx, demo, or cinematic, check to make sure the level exists
  563.     map = Cmd_Argv(1);
  564.     if (!strstr (map, "."))
  565.     {
  566.         Com_sprintf (expanded, sizeof(expanded), "maps/%s.bsp", map);
  567.         if (FS_LoadFile (expanded, NULL) == -1)
  568.         {
  569.             Com_Printf ("Can't find %s\n", expanded);
  570.             return;
  571.         }
  572.     }
  573.  
  574.     sv.state = ss_dead;        // don't save current level when changing
  575.     SV_WipeSavegame("current");
  576.     SV_GameMap_f ();
  577. }
  578.  
  579. /*
  580. =====================================================================
  581.  
  582.   SAVEGAMES
  583.  
  584. =====================================================================
  585. */
  586.  
  587.  
  588. /*
  589. ==============
  590. SV_Loadgame_f
  591.  
  592. ==============
  593. */
  594. void SV_Loadgame_f (void)
  595. {
  596.     char    name[MAX_OSPATH];
  597.     FILE    *f;
  598.     char    *dir;
  599.  
  600.     if (Cmd_Argc() != 2)
  601.     {
  602.         Com_Printf ("USAGE: loadgame <directory>\n");
  603.         return;
  604.     }
  605.  
  606.     Com_Printf ("Loading game...\n");
  607.  
  608.     dir = Cmd_Argv(1);
  609.     if (strstr (dir, "..") || strstr (dir, "/") || strstr (dir, "\\") )
  610.     {
  611.         Com_Printf ("Bad savedir.\n");
  612.     }
  613.  
  614.     // make sure the server.ssv file exists
  615.     Com_sprintf (name, sizeof(name), "%s/save/%s/server.ssv", FS_Gamedir(), Cmd_Argv(1));
  616.     f = fopen (name, "rb");
  617.     if (!f)
  618.     {
  619.         Com_Printf ("No such savegame: %s\n", name);
  620.         return;
  621.     }
  622.     fclose (f);
  623.  
  624.     SV_CopySaveGame (Cmd_Argv(1), "current");
  625.  
  626.     SV_ReadServerFile ();
  627.  
  628.     // go to the map
  629.     sv.state = ss_dead;        // don't save current level when changing
  630.     SV_Map (false, svs.mapcmd, true);
  631. }
  632.  
  633.  
  634.  
  635. /*
  636. ==============
  637. SV_Savegame_f
  638.  
  639. ==============
  640. */
  641. void SV_Savegame_f (void)
  642. {
  643.     char    *dir;
  644.  
  645.     if (sv.state != ss_game)
  646.     {
  647.         Com_Printf ("You must be in a game to save.\n");
  648.         return;
  649.     }
  650.  
  651.     if (Cmd_Argc() != 2)
  652.     {
  653.         Com_Printf ("USAGE: savegame <directory>\n");
  654.         return;
  655.     }
  656.  
  657.     if (Cvar_VariableValue("deathmatch"))
  658.     {
  659.         Com_Printf ("Can't savegame in a deathmatch\n");
  660.         return;
  661.     }
  662.  
  663.     if (!strcmp (Cmd_Argv(1), "current"))
  664.     {
  665.         Com_Printf ("Can't save to 'current'\n");
  666.         return;
  667.     }
  668.  
  669.     if (maxclients->value == 1 && svs.clients[0].edict->client->ps.stats[STAT_HEALTH] <= 0)
  670.     {
  671.         Com_Printf ("\nCan't savegame while dead!\n");
  672.         return;
  673.     }
  674.  
  675.     dir = Cmd_Argv(1);
  676.     if (strstr (dir, "..") || strstr (dir, "/") || strstr (dir, "\\") )
  677.     {
  678.         Com_Printf ("Bad savedir.\n");
  679.     }
  680.  
  681.     Com_Printf ("Saving game...\n");
  682.  
  683.     // archive current level, including all client edicts.
  684.     // when the level is reloaded, they will be shells awaiting
  685.     // a connecting client
  686.     SV_WriteLevelFile ();
  687.  
  688.     // save server state
  689.     SV_WriteServerFile (false);
  690.  
  691.     // copy it off
  692.     SV_CopySaveGame ("current", dir);
  693.  
  694.     Com_Printf ("Done.\n");
  695. }
  696.  
  697. //===============================================================
  698.  
  699. /*
  700. ==================
  701. SV_Kick_f
  702.  
  703. Kick a user off of the server
  704. ==================
  705. */
  706. void SV_Kick_f (void)
  707. {
  708.     if (!svs.initialized)
  709.     {
  710.         Com_Printf ("No server running.\n");
  711.         return;
  712.     }
  713.  
  714.     if (Cmd_Argc() != 2)
  715.     {
  716.         Com_Printf ("Usage: kick <userid>\n");
  717.         return;
  718.     }
  719.  
  720.     if (!SV_SetPlayer ())
  721.         return;
  722.  
  723.     SV_BroadcastPrintf (PRINT_HIGH, "%s was kicked\n", sv_client->name);
  724.     // print directly, because the dropped client won't get the
  725.     // SV_BroadcastPrintf message
  726.     SV_ClientPrintf (sv_client, PRINT_HIGH, "You were kicked from the game\n");
  727.     SV_DropClient (sv_client);
  728.     sv_client->lastmessage = svs.realtime;    // min case there is a funny zombie
  729. }
  730.  
  731.  
  732. /*
  733. ================
  734. SV_Status_f
  735. ================
  736. */
  737. void SV_Status_f (void)
  738. {
  739.     int            i, j, l;
  740.     client_t    *cl;
  741.     char        *s;
  742.     int            ping;
  743.     if (!svs.clients)
  744.     {
  745.         Com_Printf ("No server running.\n");
  746.         return;
  747.     }
  748.     Com_Printf ("map              : %s\n", sv.name);
  749.  
  750.     Com_Printf ("num score ping name            lastmsg address               qport \n");
  751.     Com_Printf ("--- ----- ---- --------------- ------- --------------------- ------\n");
  752.     for (i=0,cl=svs.clients ; i<maxclients->value; i++,cl++)
  753.     {
  754.         if (!cl->state)
  755.             continue;
  756.         Com_Printf ("%3i ", i);
  757.         Com_Printf ("%5i ", cl->edict->client->ps.stats[STAT_FRAGS]);
  758.  
  759.         if (cl->state == cs_connected)
  760.             Com_Printf ("CNCT ");
  761.         else if (cl->state == cs_zombie)
  762.             Com_Printf ("ZMBI ");
  763.         else
  764.         {
  765.             ping = cl->ping < 9999 ? cl->ping : 9999;
  766.             Com_Printf ("%4i ", ping);
  767.         }
  768.  
  769.         Com_Printf ("%s", cl->name);
  770.         l = 16 - strlen(cl->name);
  771.         for (j=0 ; j<l ; j++)
  772.             Com_Printf (" ");
  773.  
  774.         Com_Printf ("%7i ", svs.realtime - cl->lastmessage );
  775.  
  776.         s = NET_AdrToString ( cl->netchan.remote_address);
  777.         Com_Printf ("%s", s);
  778.         l = 22 - strlen(s);
  779.         for (j=0 ; j<l ; j++)
  780.             Com_Printf (" ");
  781.         
  782.         Com_Printf ("%5i", cl->netchan.qport);
  783.  
  784.         Com_Printf ("\n");
  785.     }
  786.     Com_Printf ("\n");
  787. }
  788.  
  789. /*
  790. ==================
  791. SV_ConSay_f
  792. ==================
  793. */
  794. void SV_ConSay_f(void)
  795. {
  796.     client_t *client;
  797.     int        j;
  798.     char    *p;
  799.     char    text[1024];
  800.  
  801.     if (Cmd_Argc () < 2)
  802.         return;
  803.  
  804.     strcpy (text, "console: ");
  805.     p = Cmd_Args();
  806.  
  807.     if (*p == '"')
  808.     {
  809.         p++;
  810.         p[strlen(p)-1] = 0;
  811.     }
  812.  
  813.     strcat(text, p);
  814.  
  815.     for (j = 0, client = svs.clients; j < maxclients->value; j++, client++)
  816.     {
  817.         if (client->state != cs_spawned)
  818.             continue;
  819.         SV_ClientPrintf(client, PRINT_CHAT, "%s\n", text);
  820.     }
  821. }
  822.  
  823.  
  824. /*
  825. ==================
  826. SV_Heartbeat_f
  827. ==================
  828. */
  829. void SV_Heartbeat_f (void)
  830. {
  831.     svs.last_heartbeat = -9999999;
  832. }
  833.  
  834.  
  835. /*
  836. ===========
  837. SV_Serverinfo_f
  838.  
  839.   Examine or change the serverinfo string
  840. ===========
  841. */
  842. void SV_Serverinfo_f (void)
  843. {
  844.     Com_Printf ("Server info settings:\n");
  845.     Info_Print (Cvar_Serverinfo());
  846. }
  847.  
  848.  
  849. /*
  850. ===========
  851. SV_DumpUser_f
  852.  
  853. Examine all a users info strings
  854. ===========
  855. */
  856. void SV_DumpUser_f (void)
  857. {
  858.     if (Cmd_Argc() != 2)
  859.     {
  860.         Com_Printf ("Usage: info <userid>\n");
  861.         return;
  862.     }
  863.  
  864.     if (!SV_SetPlayer ())
  865.         return;
  866.  
  867.     Com_Printf ("userinfo\n");
  868.     Com_Printf ("--------\n");
  869.     Info_Print (sv_client->userinfo);
  870.  
  871. }
  872.  
  873.  
  874. /*
  875. ==============
  876. SV_ServerRecord_f
  877.  
  878. Begins server demo recording.  Every entity and every message will be
  879. recorded, but no playerinfo will be stored.  Primarily for demo merging.
  880. ==============
  881. */
  882. void SV_ServerRecord_f (void)
  883. {
  884.     char    name[MAX_OSPATH];
  885.     char    buf_data[32768];
  886.     sizebuf_t    buf;
  887.     int        len;
  888.     int        i;
  889.  
  890.     if (Cmd_Argc() != 2)
  891.     {
  892.         Com_Printf ("serverrecord <demoname>\n");
  893.         return;
  894.     }
  895.  
  896.     if (svs.demofile)
  897.     {
  898.         Com_Printf ("Already recording.\n");
  899.         return;
  900.     }
  901.  
  902.     if (sv.state != ss_game)
  903.     {
  904.         Com_Printf ("You must be in a level to record.\n");
  905.         return;
  906.     }
  907.  
  908.     //
  909.     // open the demo file
  910.     //
  911.     Com_sprintf (name, sizeof(name), "%s/demos/%s.dm2", FS_Gamedir(), Cmd_Argv(1));
  912.  
  913.     Com_Printf ("recording to %s.\n", name);
  914.     FS_CreatePath (name);
  915.     svs.demofile = fopen (name, "wb");
  916.     if (!svs.demofile)
  917.     {
  918.         Com_Printf ("ERROR: couldn't open.\n");
  919.         return;
  920.     }
  921.  
  922.     // setup a buffer to catch all multicasts
  923.     SZ_Init (&svs.demo_multicast, svs.demo_multicast_buf, sizeof(svs.demo_multicast_buf));
  924.  
  925.     //
  926.     // write a single giant fake message with all the startup info
  927.     //
  928.     SZ_Init (&buf, buf_data, sizeof(buf_data));
  929.  
  930.     //
  931.     // serverdata needs to go over for all types of servers
  932.     // to make sure the protocol is right, and to set the gamedir
  933.     //
  934.     // send the serverdata
  935.     MSG_WriteByte (&buf, svc_serverdata);
  936.     MSG_WriteLong (&buf, PROTOCOL_VERSION);
  937.     MSG_WriteLong (&buf, svs.spawncount);
  938.     // 2 means server demo
  939.     MSG_WriteByte (&buf, 2);    // demos are always attract loops
  940.     MSG_WriteString (&buf, Cvar_VariableString ("gamedir"));
  941.     MSG_WriteShort (&buf, -1);
  942.     // send full levelname
  943.     MSG_WriteString (&buf, sv.configstrings[CS_NAME]);
  944.  
  945.     for (i=0 ; i<MAX_CONFIGSTRINGS ; i++)
  946.         if (sv.configstrings[i][0])
  947.         {
  948.             MSG_WriteByte (&buf, svc_configstring);
  949.             MSG_WriteShort (&buf, i);
  950.             MSG_WriteString (&buf, sv.configstrings[i]);
  951.         }
  952.  
  953.     // write it to the demo file
  954.     Com_DPrintf ("signon message length: %i\n", buf.cursize);
  955.     len = LittleLong (buf.cursize);
  956.     fwrite (&len, 4, 1, svs.demofile);
  957.     fwrite (buf.data, buf.cursize, 1, svs.demofile);
  958.  
  959.     // the rest of the demo file will be individual frames
  960. }
  961.  
  962.  
  963. /*
  964. ==============
  965. SV_ServerStop_f
  966.  
  967. Ends server demo recording
  968. ==============
  969. */
  970. void SV_ServerStop_f (void)
  971. {
  972.     if (!svs.demofile)
  973.     {
  974.         Com_Printf ("Not doing a serverrecord.\n");
  975.         return;
  976.     }
  977.     fclose (svs.demofile);
  978.     svs.demofile = NULL;
  979.     Com_Printf ("Recording completed.\n");
  980. }
  981.  
  982.  
  983. /*
  984. ===============
  985. SV_KillServer_f
  986.  
  987. Kick everyone off, possibly in preparation for a new game
  988.  
  989. ===============
  990. */
  991. void SV_KillServer_f (void)
  992. {
  993.     if (!svs.initialized)
  994.         return;
  995.     SV_Shutdown ("Game Over.\n", false);
  996.     NET_Config ( false );    // close network sockets
  997. }
  998.  
  999. /*
  1000. ===============
  1001. SV_ServerCommand_f
  1002.  
  1003. Let the game dll handle a command
  1004. ===============
  1005. */
  1006. void SV_ServerCommand_f (void)
  1007. {
  1008.     if (!ge)
  1009.     {
  1010.         Com_Printf ("No game loaded.\n");
  1011.         return;
  1012.     }
  1013.  
  1014.     ge->ServerCommand();
  1015. }
  1016.  
  1017. //===========================================================
  1018.  
  1019. /*
  1020. ==================
  1021. SV_InitOperatorCommands
  1022. ==================
  1023. */
  1024. void SV_InitOperatorCommands (void)
  1025. {
  1026.     Cmd_AddCommand ("heartbeat", SV_Heartbeat_f);
  1027.     Cmd_AddCommand ("kick", SV_Kick_f);
  1028.     Cmd_AddCommand ("status", SV_Status_f);
  1029.     Cmd_AddCommand ("serverinfo", SV_Serverinfo_f);
  1030.     Cmd_AddCommand ("dumpuser", SV_DumpUser_f);
  1031.  
  1032.     Cmd_AddCommand ("map", SV_Map_f);
  1033.     Cmd_AddCommand ("demomap", SV_DemoMap_f);
  1034.     Cmd_AddCommand ("gamemap", SV_GameMap_f);
  1035.     Cmd_AddCommand ("setmaster", SV_SetMaster_f);
  1036.  
  1037.     if ( dedicated->value )
  1038.         Cmd_AddCommand ("say", SV_ConSay_f);
  1039.  
  1040.     Cmd_AddCommand ("serverrecord", SV_ServerRecord_f);
  1041.     Cmd_AddCommand ("serverstop", SV_ServerStop_f);
  1042.  
  1043.     Cmd_AddCommand ("save", SV_Savegame_f);
  1044.     Cmd_AddCommand ("load", SV_Loadgame_f);
  1045.  
  1046.     Cmd_AddCommand ("killserver", SV_KillServer_f);
  1047.  
  1048.     Cmd_AddCommand ("sv", SV_ServerCommand_f);
  1049. }
  1050.  
  1051.